package com.jzo2o.orders.manager.service.impl;

import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.jzo2o.api.customer.dto.response.AddressBookResDTO;
import com.jzo2o.api.foundations.dto.response.ServeAggregationResDTO;
import com.jzo2o.api.orders.dto.request.OrderCancelReqDTO;
import com.jzo2o.api.trade.NativePayApi;
import com.jzo2o.api.trade.TradingApi;
import com.jzo2o.api.trade.dto.request.NativePayReqDTO;
import com.jzo2o.api.trade.dto.response.NativePayResDTO;
import com.jzo2o.api.trade.dto.response.TradingResDTO;
import com.jzo2o.api.trade.enums.PayChannelEnum;
import com.jzo2o.api.trade.enums.TradingStateEnum;
import com.jzo2o.common.expcetions.BadRequestException;
import com.jzo2o.common.expcetions.DBException;
import com.jzo2o.common.utils.BeanUtils;
import com.jzo2o.common.utils.DateUtils;
import com.jzo2o.common.utils.ObjectUtils;
import com.jzo2o.mvc.utils.UserContext;
import com.jzo2o.orders.base.enums.OrderPayStatusEnum;
import com.jzo2o.orders.base.enums.OrderStatusEnum;
import com.jzo2o.orders.base.mapper.OrdersMapper;
import com.jzo2o.orders.base.model.domain.Orders;
import com.jzo2o.orders.base.model.domain.OrdersCanceled;
import com.jzo2o.orders.manager.client.CustomerClient;
import com.jzo2o.orders.manager.client.FoundationsClient;
import com.jzo2o.orders.manager.model.dto.request.OrdersPayReqDTO;
import com.jzo2o.orders.manager.model.dto.request.PlaceOrderReqDTO;
import com.jzo2o.orders.manager.model.dto.response.OrdersPayResDTO;
import com.jzo2o.orders.manager.model.dto.response.PlaceOrderResDTO;
import com.jzo2o.orders.manager.porperties.TradeProperties;
import com.jzo2o.orders.manager.service.IOrdersCanceledService;
import com.jzo2o.orders.manager.service.IOrdersCreateService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.Arrays;
import java.util.List;

/**
 * <p>
 * 下单服务类
 * </p>
 *
 * @author itcast
 * @since 2023-07-10
 */
@Slf4j
@Service
public class OrdersCreateServiceImpl extends ServiceImpl<OrdersMapper, Orders> implements IOrdersCreateService {
    @Autowired
    private CustomerClient customerClient;
    
    @Autowired
    private FoundationsClient foundationsClient;
    
    @Autowired
    private IOrdersCreateService owner;
    
    @Autowired
    private RedisTemplate redisTemplate;
    
    @Resource
    private TradeProperties tradeProperties;
    
    @Resource
    private NativePayApi nativePayApi;
    
    @Resource
    private TradingApi tradingApi;
    
    @Resource
    private IOrdersCanceledService ordersCanceledService;
    
    /**
     * 自增序列号的redisKey
     */
    private static final String ID_SERIAL_NUM_KEY = "ID_SERIAL_NUM_KEY";
    
    /**
     * 下单
     *
     * @param placeOrderReqDTO 下单请求参数
     * @return 下单结果
     */
    @Override
    public PlaceOrderResDTO placeOrder(PlaceOrderReqDTO placeOrderReqDTO) {
        //1. 调用feign接口（foundations服务的）查询服务信息
        ServeAggregationResDTO serveDetail = foundationsClient.findServeDetailById(placeOrderReqDTO.getServeId());
        if (serveDetail == null) {
            throw new BadRequestException("服务信息查询失败");
        }
        //2. 调用feign接口（customer服务的）查询地址信息
        AddressBookResDTO address = customerClient.findAddressDetailById(placeOrderReqDTO.getAddressBookId());
        if (address == null) {
            throw new BadRequestException("地址信息查询失败");
        }
        
        //3. 封装订单对象信息
        Orders orders = new Orders();
        
        //3.1 订单基本信息封装
        //订单号：19位组成结构 - 2位年+2位月+2位日+13位序号
        orders.setId(this.generateOrderId());
        // 订单状态代码，下单后状态是未支付状态
        orders.setOrdersStatus(OrderStatusEnum.NO_PAY.getStatus());
        // 订单支付状态代码，下单后状态是未支付状态 2：待支付，4：支付成功
        orders.setPayStatus(OrderPayStatusEnum.NO_PAY.getStatus());
        //用户端是否展示，1：展示，0：隐藏，默认展示
        orders.setDisplay(1);
        //服务开始时间
        orders.setServeStartTime(placeOrderReqDTO.getServeStartTime());
        //根据服务开始时间转为毫秒时间戳+订单后5位
        orders.setSortBy(DateUtils.toEpochMilli(placeOrderReqDTO.getServeStartTime()) + orders.getId() % 10000);
        
        
        //3.2 下单人信息封装
        orders.setUserId(UserContext.currentUserId());
        String serveAddress = new StringBuilder()
                .append(address.getProvince())
                .append(address.getCity())
                .append(address.getCounty())
                .append(address.getAddress())
                .toString();
        orders.setServeAddress(serveAddress);
        orders.setContactsPhone(address.getPhone());
        orders.setContactsName(address.getName());
        orders.setLat(address.getLat());
        orders.setLon(address.getLon());
        
        //3.3  TODO支付相关信息（暂时不处理）
        
        //3.4 服务信息封装
        orders.setServeId(serveDetail.getId());
        orders.setPrice(serveDetail.getPrice());
        orders.setCityCode(serveDetail.getCityCode());
        orders.setServeItemId(serveDetail.getServeItemId());
        orders.setServeItemName(serveDetail.getServeItemName());
        orders.setServeItemImg(serveDetail.getServeItemImg());
        orders.setServeTypeId(serveDetail.getServeTypeId());
        orders.setServeTypeName(serveDetail.getServeTypeName());
        orders.setUnit(serveDetail.getUnit());
        
        //3.5 价格信息封装
        orders.setPurNum(placeOrderReqDTO.getPurNum());
        orders.setTotalAmount(serveDetail.getPrice().multiply(BigDecimal.valueOf(placeOrderReqDTO.getPurNum())));
        orders.setDiscountAmount(BigDecimal.ZERO);
        orders.setRealPayAmount(orders.getTotalAmount().subtract(orders.getDiscountAmount()));
        
        //4. 创建订单到订单表
        owner.add(orders);
        
        return new PlaceOrderResDTO(orders.getId());
    }
    
    @Transactional(rollbackFor = Exception.class)
    public void add(Orders orders) {
        boolean result = this.save(orders);
        if (!result) {
            throw new DBException("服务预约下单失败");
        }
    }
    
    /**
     * 生成唯一订单ID（订单号）： 年月日+13位自增序列号，长度共计19位
     *
     * @return
     */
    public Long generateOrderId() {
        
        //获取订单号的初始值：yyMMdd格式的日期
        Long idInit = DateUtils.getFormatDate(LocalDateTime.now(), "yyMMdd") * 10000000000000L;
        
        //基于Redis的自增值
        Long increment = redisTemplate.opsForValue().increment(ID_SERIAL_NUM_KEY);
        
        return idInit + increment;
    }
    
    
    /**
     * 订单支付
     *
     * @param id              订单id
     * @param ordersPayReqDTO 订单支付参数
     * @return 订单支付结果
     */
    @Override
    public OrdersPayResDTO payOrder(Long id, OrdersPayReqDTO ordersPayReqDTO) {
        //1 查询订单信息
        Orders orders = this.lambdaQuery().eq(Orders::getId, id).one();
        if (orders == null) {
            log.error("订单不存在");
            throw new RuntimeException("订单不存在");
        }
        //2 校验订单是否已经支付成功
        if (orders.getPayStatus() == OrderPayStatusEnum.PAY_SUCCESS.getStatus() && ObjectUtils.isNotNull(orders.getTradingOrderNo())) {
            OrdersPayResDTO ordersPayResDTO = BeanUtils.copyBean(orders, OrdersPayResDTO.class);
            ordersPayResDTO.setProductOrderNo(id);
            return ordersPayResDTO;
        }
        //3 组装请求参数
        NativePayReqDTO nativePayReqDTO = new NativePayReqDTO();
        //3.1 根据支付渠道获取商户号
        Long enterpriseId = ObjectUtils.equals(ordersPayReqDTO.getTradingChannel(), PayChannelEnum.WECHAT_PAY) ? tradeProperties.getWechatEnterpriseId()
                : tradeProperties.getAliEnterpriseId();
        nativePayReqDTO.setEnterpriseId(enterpriseId);
        nativePayReqDTO.setProductAppId(tradeProperties.getWechatEnterpriseId().toString());
        nativePayReqDTO.setProductOrderNo(id);
        
        nativePayReqDTO.setTradingChannel(ordersPayReqDTO.getTradingChannel());
        nativePayReqDTO.setMemo(orders.getServeItemName());
        //nativePayReqDTO.setTradingAmount(orders.getTotalAmount());
        nativePayReqDTO.setTradingAmount(BigDecimal.valueOf(0.01));
        boolean changeChannel = ObjectUtils.isNotNull(orders.getTradingChannel())
                && ObjectUtils.notEqual(orders.getTradingChannel(), ordersPayReqDTO.getTradingChannel());
        nativePayReqDTO.setChangeChannel(changeChannel);
        //4 调用支付服务
        NativePayResDTO nativePayResDTO = nativePayApi.createDownLineTrading(nativePayReqDTO);
        if (ObjectUtils.isNull(nativePayResDTO)) {
            throw new BadRequestException("调用支付服务的支付接口失败");
        }
        //5 更新订单数据
        owner.updateOrders(id, nativePayResDTO);
        
        //6 构建订单支付结果
        OrdersPayResDTO ordersPayResDTO = BeanUtils.copyBean(nativePayResDTO, OrdersPayResDTO.class);
        ordersPayResDTO.setPayStatus(orders.getPayStatus());
        return ordersPayResDTO;
        
        
    }
    
    /**
     * 更新订单数据
     *
     * @param orderId         订单id
     * @param nativePayResDTO 支付结果
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public void updateOrders(Long orderId, NativePayResDTO nativePayResDTO) {
        boolean result = this.lambdaUpdate().eq(Orders::getId, orderId)
                .set(Orders::getTradingChannel, nativePayResDTO.getTradingChannel())
                .set(Orders::getTradingOrderNo, nativePayResDTO.getTradingOrderNo())
                .update();
        if (!result) {
            throw new DBException("支付失败");
        }
    }
    
    /**
     * 订单支付结果查询
     *
     * @param id 订单id
     * @return 支付结果
     */
    @Override
    public OrdersPayResDTO payResult(Long id) {
        //1 查询订单信息
        Orders orders = this.lambdaQuery().eq(Orders::getId, id).one();
        if (orders == null) {
            throw new RuntimeException("订单不存在");
        }
        
        //2 判断是否已有交易单号
        if (ObjectUtils.isNull(orders.getTradingOrderNo())) {
            throw new RuntimeException("非法订单");
        }
        
        //3 判断是否已支付成功, 如果支付成功直接返回订单信息
        OrdersPayResDTO ordersPayResDTO = BeanUtils.copyBean(orders, OrdersPayResDTO.class);
        ordersPayResDTO.setProductOrderNo(id);
        if (ObjectUtils.equals(orders.getPayStatus(), OrderPayStatusEnum.PAY_SUCCESS.getStatus())) {
            return ordersPayResDTO;
        }
        
        //4 调用支付服务查询支付结果
        TradingResDTO tradingResDTO = tradingApi.findTradResultByTradingOrderNo(orders.getTradingOrderNo());
        if (ObjectUtils.isNull(tradingResDTO)) {
            throw new BadRequestException("调用支付服务的查询接口失败");
        }
        
        //5.更新订单表信息（支付时间、第三方交易号、订单状态、支付状态）
        if (ObjectUtils.equals(tradingResDTO.getTradingState(), TradingStateEnum.YJS)) {
            owner.paySuccess(id, tradingResDTO.getTransactionId(), tradingResDTO.getPaySuccessTime());
            
            //6.封装响应数据
            ordersPayResDTO.setQrCode(tradingResDTO.getQrCode());
            ordersPayResDTO.setPayStatus(OrderPayStatusEnum.PAY_SUCCESS.getStatus());
        }
        
        
        return ordersPayResDTO;
        
    }
    
    
    @Transactional(rollbackFor = Exception.class)
    public void paySuccess(Long orderId, String transactionId, LocalDateTime payTime) {
        boolean result = this.lambdaUpdate()
                .eq(Orders::getId, orderId)
                .set(Orders::getPayTime, payTime)
                .set(Orders::getTransactionId, transactionId)
                .set(Orders::getOrdersStatus, OrderStatusEnum.DISPATCHING.getStatus())
                .set(Orders::getPayStatus, OrderPayStatusEnum.PAY_SUCCESS.getStatus())
                .update();
        if (!result) {
            throw new DBException("更新订单失败");
        }
        
    }
    
    
    /**
     * 订单取消
     *
     * @param orderCancelReqDTO 订单id,取消原因
     */
    @Override
    public void cancelOrder(OrderCancelReqDTO orderCancelReqDTO) {
        // 1 查询订单信息
        Orders orders = this.lambdaQuery().eq(Orders::getId, orderCancelReqDTO.getId()).one();
        if (orders == null) {
            throw new RuntimeException("订单不存在");
        }
        Integer status = orders.getOrdersStatus();
        // 2 判断订单是否状态是否支持取消(未支付或派单中)
        Integer noPayStatus = OrderStatusEnum.NO_PAY.getStatus();
        Integer dispatchingStatus = OrderStatusEnum.DISPATCHING.getStatus();
        List<Integer> statusList = Arrays.asList(noPayStatus, dispatchingStatus);
        if (!statusList.contains(status)){
            throw new RuntimeException("订单状态不支持取消");
        }
        
        // 3 订单取消
        if (status.equals(noPayStatus)) {
            //3.1 如果是未支付订单,直接取消更新订单状态
            owner.cancelOrderByNoPay(orderCancelReqDTO.getId(), orderCancelReqDTO.getCancelReason(), false);
        }else {
            // TODO 3.2 派单中的订单取消-关闭订单且退款
        }
        
        
    }
    
    /**
     * 取消未支付订单
     *
     * @param orderId
     * @param cancelReason
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void cancelOrderByNoPay(Long orderId, String cancelReason , boolean isAutoCancel) {
        //1. 添加取消记录
        OrdersCanceled ordersCanceled = new OrdersCanceled();
        ordersCanceled.setId(orderId);
        ordersCanceled.setCancelReason(cancelReason);
        ordersCanceled.setCancelTime(LocalDateTime.now());
        if (!isAutoCancel){
            ordersCanceled.setCancellerId(UserContext.currentUserId());
            ordersCanceled.setCancellerType(UserContext.currentUser().getUserType());
            ordersCanceled.setCancelerName(UserContext.currentUser().getName());
        }
        boolean resultCancel = ordersCanceledService.save(ordersCanceled);
        if(!resultCancel) {
            throw new DBException("添加取消记录失败");
        }
        
        //2. 更新订单状态
        boolean resultOrder = this.lambdaUpdate()
                .eq(Orders::getId, orderId)
                .set(Orders::getOrdersStatus, OrderStatusEnum.CANCELED.getStatus())
                .update();
        if(!resultOrder) {
            throw new DBException("更新订单失败");
        }
    }
}
